
#include "Dimension.h"
#include <sstream>

Unit::Unit(unsigned int conversionFactor, UnitList &list, const char *name)
    : conversionFactor(conversionFactor), list(list), name(name)
{
    int i = 0;
    for (UnitList::iterator it = this->list.begin(); i < this->list.size(); i ++, it ++)
    {
        if (this->toBaseUnit(1) > (*it)->toBaseUnit(1))
        {
            this->list.insert(it, this);
            break;
        }
    }
    if (i == this->list.size())
    {
        this->list.push_back(this);
    }
}

unsigned int Unit::toBaseUnit(unsigned int amount) const
{
    return amount * conversionFactor;
}

bool Unit::isSameType(const Unit& unit) const
{
    return &this->list == &unit.list;
}

const UnitList& Unit::getUnitList() const
{
    return list;
}

const std::string& Unit::getName() const
{
    return name;
}

Dimension::Dimension(unsigned int amount, const Unit& unit) : amount(amount), unit(unit)
{
}

bool Dimension::operator==(const Dimension& dimension) const
{
    return this->unit.isSameType(dimension.unit)
           && this->getBaseAmount() == dimension.getBaseAmount();
}

bool Dimension::operator!=(const Dimension& dimension) const
{
    return !(*this == dimension);
}

Dimension Dimension::operator+(const Dimension& dimension) const throw (int)
{
    unsigned int amountInBase = 0;
    
    if (!this->unit.isSameType(dimension.unit))
    {
        throw DIFFERENT_UNIT_TYPE;
    }

    amountInBase = this->getBaseAmount() + dimension.getBaseAmount();
    if (this->unit.toBaseUnit(1) > dimension.unit.toBaseUnit(1))
    {
        return Dimension(amountInBase / dimension.unit.toBaseUnit(1), dimension.unit);
    }
    else
    {
        return Dimension(amountInBase / this->unit.toBaseUnit(1), this->unit);
    }
}

unsigned int Dimension::getBaseAmount() const
{
    return unit.toBaseUnit(amount);
}

std::string Dimension::toString() const
{
    unsigned int amountInBase = unit.toBaseUnit(amount);
    unsigned int quotient = 0;
    std::string res;
    bool isFirstUnit = true;
    
    for (UnitList::const_iterator it = unit.getUnitList().begin();
         it != unit.getUnitList().end() && amountInBase;
         it ++)
    {
        if ((quotient = amountInBase / (*it)->toBaseUnit(1)) != 0)
        {
            if (!isFirstUnit)
            {
                res += " ";
            }
            isFirstUnit = false;
            std::stringstream tmp;
            tmp << quotient;
            res += tmp.str() + " " + (*it)->getName();
        }
        amountInBase -= quotient * (*it)->toBaseUnit(1);
    }
    
    if (isFirstUnit)
    {
        res = toStringInSmallestUnit();
    }
    
    return res;
}

std::string Dimension::toStringInSmallestUnit() const
{
    unsigned int amountInBase = unit.toBaseUnit(amount);
    std::stringstream tmp;
    
    tmp << amountInBase / unit.getUnitList().back()->toBaseUnit(1);
    return (tmp.str() += " ") += unit.getUnitList().back()->getName();
}

